package com.xgit.iov.common.redis;


import com.xgit.iov.common.redis.properties.CacheManagerProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * redis自动装配
 */
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(CacheManagerProperties.class)
@AutoConfigureBefore(RedisAutoConfiguration.class)
@EnableCaching
public class RedisAutoConfig {

	private final RedisConnectionFactory factory;

	@Autowired
	private CacheManagerProperties cacheManagerProperties;

	@Bean
	public RedisSerializer<String> redisKeySerializer() {
		return RedisSerializer.string();
	}

	@Bean
	public RedisSerializer<Object> redisValueSerializer() {
		return RedisSerializer.java();
	}

	/**
	 * RedisTemplate配置
	 */
	@Bean
	public RedisTemplate<String, Object> redisTemplate(RedisSerializer<String> redisKeySerializer, RedisSerializer<Object> redisValueSerializer) {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setConnectionFactory(factory);

		redisTemplate.setDefaultSerializer(redisValueSerializer);
		redisTemplate.setKeySerializer(redisKeySerializer);
		redisTemplate.setHashKeySerializer(redisKeySerializer);
		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}

	@Bean(name = "cacheManager")
	@Primary
	public CacheManager cacheManager(RedisSerializer<String> redisKeySerializer, RedisSerializer<Object> redisValueSerializer) {
		RedisCacheConfiguration difConf = getDefConf(redisKeySerializer, redisValueSerializer).entryTtl(Duration.ofHours(1));

		//自定义的缓存过期时间配置
		int configSize = cacheManagerProperties.getConfigs() == null ? 0 : cacheManagerProperties.getConfigs().size();
		Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>(configSize);
		if (configSize > 0) {
			cacheManagerProperties.getConfigs().forEach(e -> {
				RedisCacheConfiguration conf = getDefConf(redisKeySerializer, redisValueSerializer).entryTtl(Duration.ofSeconds(e.getSecond()));
				redisCacheConfigurationMap.put(e.getKey(), conf);
			});
		}

		return RedisCacheManager.builder(factory)
			.cacheDefaults(difConf)
			.withInitialCacheConfigurations(redisCacheConfigurationMap)
			.build();
	}

	@Bean
	public KeyGenerator keyGenerator() {
		return (target, method, objects) -> {
			StringBuilder sb = new StringBuilder();
			sb.append(target.getClass().getName());
			sb.append(":" + method.getName() + ":");
			for (Object obj : objects) {
				sb.append(obj.toString());
			}
			return sb.toString();
		};
	}

	private RedisCacheConfiguration getDefConf(RedisSerializer<String> redisKeySerializer, RedisSerializer<Object> redisValueSerializer) {
		return RedisCacheConfiguration.defaultCacheConfig()
			.disableCachingNullValues()
			.computePrefixWith(cacheName -> "cache".concat(":").concat(cacheName).concat(":"))
			.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisKeySerializer))
			.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisValueSerializer));
	}

}
